package com.puzzlebazar.client.user;

import java.util.Iterator;
import java.util.Vector;

import com.google.gwt.user.client.rpc.AsyncCallback;
import com.puzzlebazar.client.main.model.AutoLink;
import com.puzzlebazar.client.main.model.LinkGroup;
import com.puzzlebazar.client.services.ServiceFactory;
import com.puzzlebazar.client.services.UserInfoServiceAsync;
import com.puzzlebazar.client.util.AsyncCallbackLock;
import com.puzzlebazar.client.util.ConnectionLock;
import com.puzzlebazar.client.util.ObservableImpl;

public class UserModel extends ObservableImpl  {

	public static UserModel singleton = null;
	
	public static UserModel get() {
		if( singleton == null )
			singleton = new UserModel();
		return singleton;
	}
	
	private UserInfo userInfo = null;

	private final Vector<LinkGroup> linkGroups = new Vector<LinkGroup>();
	
	/**
	 * Construct a default user model, which has one default link group
	 */
	private UserModel() {
		linkGroups.add( new LinkGroup("Default links") );
	}
	
	/**
	 * Sets the new user information (Must call update observers) 
	 * 
	 * @param userInfo The new user information
	 */
	private void setUserInfo(UserInfo userInfo) {
		this.userInfo = userInfo;
		setChanged();
	}

	/**
	 * @return The user information
	 */
	public UserInfo getUserInfo() {
		return userInfo;
	}

	/**
	 * Adds a group of links to the model (Must call update observers) 
	 * 
	 * @param linkGroupName The name of the new link group to add
	 */
	public void addLinkGroup(String linkGroupName) {
		linkGroups.add( new LinkGroup(linkGroupName) );
		setChanged();
	}

	/**
	 * Adds a link to a default group (Must call update observers) 
	 * 
	 * @param link The link to add
	 */
	public void addDefaultLink(AutoLink link) {
		LinkGroup defaultGroup = linkGroups.get(0);
		
		defaultGroup.addLink( link );		
		setChanged();
	}

	/**
	 * Adds a link to a specific group (Must call update observers) 
	 * 
	 * @param groupName The group to which to add the link
	 * @param link The link to add
	 * @throws Exception
	 */
	public void addLink(String groupName, AutoLink link) throws Exception {
		
		for (Iterator<LinkGroup> iterator = linkGroups.iterator(); iterator.hasNext();) {
			LinkGroup linkGroup = iterator.next();
			if( linkGroup.getName() == groupName ) {
				linkGroup.addLink( link );
				setChanged();
				return;
			}
		}
		
		throw new Exception( "Group with name \"" + groupName + "\" not found!" );
		
	}

	/**
	 * @return The number of link groups in the model
	 */
	public int getLinkGroupCount() {
		return linkGroups.size();
	}

	/**
	 * Access a specific link group, return null if invalid index
	 * 
	 * @param index The index of the link group to access
	 * @return The requested link group, or null if the index is out-of-range
	 */
	public LinkGroup getLinkGroup(int index) {
		if( index < 0 || index >= linkGroups.size() )
			return null;
		return linkGroups.get(index);
	}

	/**
	 * Extract all the information on the currently logged in user from the server
	 * @param connectionLock  Global lock to prevent application from operating before everything is loaded
	 */
	public void loadCurrentUserInfo(ConnectionLock connectionLock) {
		UserInfoServiceAsync userService = ServiceFactory.getUserInfoService();
		userService.loadCurrentUser( new AsyncCallbackLock<UserInfo>(connectionLock){
			@Override
			public void onFailure(Throwable caught) {
				caught.printStackTrace();
				super.onFailure(caught);
			}
			@Override
			public void onSuccess(UserInfo result) {
				if( result != null ) {
					setUserInfo( result );
					notifyObservers();
				}
				super.onSuccess(result);
			}
		});
	}
	
	/**
	 * Modify user profile in the database
	 * 
	 * @param userInfo The information concerning this user
	 * @param callback The callback to execute after the modification is completed
	 */
	public void modifyUser(final UserInfo userInfo, final AsyncCallback<Integer> callback) {
		UserInfoServiceAsync userService = ServiceFactory.getUserInfoService();
		userService.modifyUser( userInfo, new AsyncCallback<Integer>() {
			@Override
			public void onFailure(Throwable caught) {
				callback.onFailure(caught);
			}
			@Override
			public void onSuccess(Integer result) {
				if( result == UserInfo.ErrorFlags.SUCCESS ) {
					setUserInfo( userInfo );
					notifyObservers();
				}
				callback.onSuccess(result);
			} });  
	}

	/**
	 * @return True if the model contains valid settings, false if not
	 */
	public boolean isValid() {
		if( userInfo == null )
			return true; // Not logged in, so valid
		return userInfo.isValid();
	}

	/**
	 * @return True if a user is logged in, false if not
	 */
	public boolean isLoggedIn() {
		return userInfo != null;
	}
	
	
	
}
